package com.review.impact;

import com.review.model.CallGraph;
import com.review.model.DiffResult;

import java.util.*;

public class ImpactAnalysisService {

	public static class ImpactResult {
		public Set<String> changedMethods = new LinkedHashSet<>();
		public Set<String> impactedMethods = new LinkedHashSet<>();
	}

	// MVP: map changed files to methods by file path and line ranges
	public ImpactResult analyze(DiffResult diff, CallGraph graph, int kHops) {
		ImpactResult result = new ImpactResult();
		Map<String, List<CallGraph.MethodNode>> fileToMethods = new HashMap<>();
		for (CallGraph.MethodNode node : graph.nodes.values()) {
			fileToMethods.computeIfAbsent(node.file, x -> new ArrayList<>()).add(node);
		}
		for (DiffResult.FileDiff fd : diff.getFiles()) {
			List<CallGraph.MethodNode> methods = fileToMethods.getOrDefault(fd.path, Collections.emptyList());
			for (CallGraph.MethodNode m : methods) {
				result.changedMethods.add(m.methodId);
			}
		}
		// BFS over call edges up to k hops
		Map<String, List<String>> outEdges = new HashMap<>();
		Map<String, List<String>> inEdges = new HashMap<>();
		for (CallGraph.CallEdge e : graph.edges) {
			outEdges.computeIfAbsent(e.caller, x -> new ArrayList<>()).add(e.callee);
			inEdges.computeIfAbsent(e.callee, x -> new ArrayList<>()).add(e.caller);
		}
		Queue<String> q = new ArrayDeque<>(result.changedMethods);
		Map<String, Integer> dist = new HashMap<>();
		for (String s : result.changedMethods) dist.put(s, 0);
		while (!q.isEmpty()) {
			String cur = q.poll();
			int d = dist.get(cur);
			if (d == kHops) continue;
			for (String nxt : outEdges.getOrDefault(cur, Collections.emptyList())) {
				if (!dist.containsKey(nxt)) { dist.put(nxt, d + 1); q.add(nxt); }
			}
			for (String prev : inEdges.getOrDefault(cur, Collections.emptyList())) {
				if (!dist.containsKey(prev)) { dist.put(prev, d + 1); q.add(prev); }
			}
		}
		result.impactedMethods.addAll(dist.keySet());
		return result;
	}
}

